package com.oxymore.jbnetapi.connectors;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.log4j.Logger;

import com.google.gson.Gson;
import com.oxymore.jbnetapi.core.ApiRequest;
import com.oxymore.jbnetapi.utils.DateUtil;
import org.apache.commons.codec.binary.Base64;

public abstract class AbstractConnector {

    private Logger logger = Logger.getLogger(AbstractConnector.class.getCanonicalName() + "\n");
    protected String baseUrl = null;
    protected String privateKey = null;
    protected String publicKey = null;
    protected boolean usingAuth = false;
    protected Gson gson = new Gson();
    protected String defaultLocale;


    public AbstractConnector(String baseUrl, String privateKey,
            String publicKey, String defaultLocale) {
        this.baseUrl = baseUrl;
        this.privateKey = privateKey;
        this.publicKey = publicKey;
        this.defaultLocale = defaultLocale;
    }

    protected URLConnection openConnection(URL requestUrl) throws Exception {
        URLConnection conn = null;
        try {
            conn = requestUrl.openConnection();
        } catch (IOException e) {
            // TODO Auto-generated catch block
        }
        return conn;
    }

    /**
     * 
     * @param url
     * @param authString
     * @return
     */
    protected String getContent(URL url, String authString) {
        HttpURLConnection conn = null;
        final StringBuilder contentResponse = new StringBuilder();
        String inputLine = null;
        logger.info("Trying to retrieve query content for url: "
                + url.toString());
        try {
            conn = (HttpURLConnection) openConnection(url);
            conn.addRequestProperty("User-Agent", "JBnetApi");
            if (authString != null && usingAuth) {
                conn.addRequestProperty("Date", DateUtil.formatDate(new Date()).toString());
                conn.addRequestProperty("Authorization", authString);
            } else {
                logger.info("No authString set, not using authentication");
            }
            try (BufferedReader in = new BufferedReader(new InputStreamReader(
                            conn.getInputStream(), "UTF-8"))) {
                while ((inputLine = in.readLine()) != null) {
                    contentResponse.append(inputLine);
                }
            }
            logger.info("Successfuly retrieved query content for url: "
                    + url.toString());
        } catch (Exception e) {
            logger.error("Failure while retrieving query content for url: "
                    + url.toString());
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    conn.getErrorStream()));
            try {
                while ((inputLine = in.readLine()) != null) {
                    contentResponse.append(inputLine);
                }
                in.close();
            } catch (IOException e1) {
                logger.error("Failure while failing, I think it's bad.");
            }
        }
        return contentResponse.toString();
    }

    protected URL getURL(ApiRequest request) {

        Boolean firstparam = true;
        URL urlResult = null;

        StringBuilder result = new StringBuilder();
        result.append(request.getPath().getPath());
        if (request.getPage() != null) {
            result.append(request.getPage().getPage());
        }
        if (request.getCustomPage() != null) {
            result.append(request.getCustomPage());
        }
        String path = result.toString();
        result = new StringBuilder();
        try {
            if (request.getParams() != null) {
                for (Entry<String, String> entry : request.getParams().entrySet()) {
                    if (result.length() > 0) {
                        if (firstparam) {
                            result.append("?");
                        } else {
                            result.append("&");
                        }
                    }
                    String key = entry.getKey();
                    String value = entry.getValue();
                    result.append(key);
                    result.append("=");
                    result.append(value);
                }
            }
            URI uriResult = new URI("https", getBaseUrl(), path,
                    result.toString(), null);
            urlResult = uriResult.toURL();
        } catch (URISyntaxException | MalformedURLException e) {
            // TODO Auto-generated catch block
        }
        return urlResult;
    }

    protected Map<String, String> getParams(ApiRequest request) {
        Map<String, String> result = new HashMap<>();
        Map<String, String> params = request.getParams();
        if (params != null) {
            result.putAll(params);
        }
        return result;
    }

    /**
     * 
     * @param request
     * @return
     */
    protected String buildAuthorization(String request) {
        if (privateKey == null || publicKey == null) {
            logger.info("No signature generated");
            return null;
        }
        String date = DateUtil.formatDate(new Date());
        StringBuilder stringToSign = new StringBuilder();
        stringToSign.append("GET").append("\n");
        stringToSign.append(date.trim()).append("\n");
        stringToSign.append(request).append("\n");
        SecretKey key = new SecretKeySpec(privateKey.getBytes(Charset.forName("UTF-8")), "HmacSha1");
        Mac mac = null;
        try {
            mac = Mac.getInstance("HmacSha1");
            mac.init(key);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            return null;
        }
        mac.update(stringToSign.toString().getBytes());
        StringBuilder result = new StringBuilder();
        result.append("BNET ").append(publicKey).append(":").append(Base64.encodeBase64(mac.doFinal()));
        logger.info("Building authorization for:\n" + stringToSign);
        logger.info("Full signature: " + result.toString());
        return result.toString();
    }

    public String getBaseUrl() {
        return baseUrl;
    }

    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /**
     * @return the privateKey
     */
    public String getPrivateKey() {
        return privateKey;
    }

    /**
     * @param privateKey
     *            the privateKey to set
     */
    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
        if (this.publicKey != null && this.privateKey != null) {
            usingAuth = true;
        }
    }

    /**
     * @return the publicKey
     */
    public String getPublicKey() {
        return publicKey;
    }

    /**
     * @param publicKey
     *            the publicKey to set
     */
    public void setPublicKey(String publicKey) {
        this.publicKey = publicKey;
        if (this.publicKey != null && this.privateKey != null) {
            usingAuth = true;
        }
    }

    /**
     * @return the defaultLocale
     */
    public String getDefaultLocale() {
        return defaultLocale;
    }

    /**
     * @param defaultLocale
     *            the defaultLocale to set
     */
    public void setDefaultLocale(String defaultLocale) {
        this.defaultLocale = defaultLocale;
    }
}
